// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/abi"
	"runtime/internal/atomic"
	"unsafe"
)

type mOS struct {
	waitsemacount uint32
}

const (
	_ESRCH       = 3
	_EWOULDBLOCK = _EAGAIN
	_ENOTSUP     = 91

	// 来自OpenBSD的sys/time。h 
	_CLOCK_REALTIME  = 0
	_CLOCK_VIRTUAL   = 1
	_CLOCK_PROF      = 2
	_CLOCK_MONOTONIC = 3
)

type sigset uint32

var sigset_all = ^sigset(0)

// 来自OpenBSD的<sys/sysctl。h> 
const (
	_CTL_KERN   = 1
	_KERN_OSREV = 3

	_CTL_HW        = 6
	_HW_NCPU       = 3
	_HW_PAGESIZE   = 7
	_HW_NCPUONLINE = 25
)

func sysctlInt(mib []uint32) (int32, bool) {
	var out int32
	nout := unsafe.Sizeof(out)
	ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
	if ret < 0 {
		return 0, false
	}
	return out, true
}

func getncpu() int32 {
	// 试试看。首先是因为hw。ncpu报告的数字是
	// 的两倍，高出OpenBSD 6.4上运行的实际CPU，超线程
	// 已禁用（hw.smt=0）。参见https:
	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPUONLINE}); ok {
		return int32(n)
	}
	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPU}); ok {
		return int32(n)
	}
	return 1
}

func getPageSize() uintptr {
	if ps, ok := sysctlInt([]uint32{_CTL_HW, _HW_PAGESIZE}); ok {
		return uintptr(ps)
	}
	return 0
}

func getOSRev() int {
	if osrev, ok := sysctlInt([]uint32{_CTL_KERN, _KERN_OSREV}); ok {
		return int(osrev)
	}
	return 0
}

// go:nosplit 
func semacreate(mp *m) {
}

// go:nosplit 
func semasleep(ns int64) int32 {
	_g_ := getg()

	// Compute sleep deadline。
	var tsp *timespec
	if ns >= 0 {
		var ts timespec
		ts.setNsec(ns + nanotime())
		tsp = &ts
	}

	for {
		v := atomic.Load(&_g_.m.waitsemacount)
		if v > 0 {
			if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
				return 0 // 获取的信号量
			}
			continue
		}

		// 睡眠至被信号唤醒或超时；如果等待计数，则中止！=0.
		// 
		// 来自OpenBSD的_thrsleep（2）手册：
		// “abort参数如果不为空，则指向将被检查的整数[…]就在阻塞之前。如果int 
		// 非零，则_thrsleep（）将立即返回EINTR 
		// 而不阻塞。“
		ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount)
		if ret == _EWOULDBLOCK {
			return -1
		}
	}
}

// go:nosplit 
func semawakeup(mp *m) {
	atomic.Xadd(&mp.waitsemacount, 1)
	ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1)
	if ret != 0 && ret != _ESRCH {
		// semawakeup可以在信号栈上调用。
		systemstack(func() {
			print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n")
		})
	}
}

func osinit() {
	ncpu = getncpu()
	physPageSize = getPageSize()
	haveMapStack = getOSRev() >= 201805 // OpenBSD 6.3 
}

var urandom_dev = []byte("/dev/urandom\x00")

// go:nosplit 
func getRandomData(r []byte) {
	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
	closefd(fd)
	extendRandom(r, int(n))
}

func goenvs() {
	goenvs_unix()
}

// 调用初始化一个新的m（包括引导程序m）。
// 在父线程（引导程序的主线程）上调用，可以分配内存。
func mpreinit(mp *m) {
	gsignalSize := int32(32 * 1024)
	if GOARCH == "mips64" {
		gsignalSize = int32(64 * 1024)
	}
	mp.gsignal = malg(gsignalSize)
	mp.gsignal.m = mp
}

// （包括bootstrap m）。
// 在新线程上调用，无法分配内存。
func minit() {
	getg().m.procid = uint64(getthrid())
	minitSignals()
}

// 从dropm调用以撤消minit的效果。
// go:nosplit 
func unminit() {
	unminitSignals()
}

// 从exitm调用，而不是从drop调用，以撤销线程拥有的
// minit、semacreate或其他资源的效果。打完电话后不要带锁。
func mdestroy(mp *m) {
}

func sigtramp()

type sigactiont struct {
	sa_sigaction uintptr
	sa_mask      uint32
	sa_flags     int32
}

// go:nosplit 
// go:nowritebarrierrec 
func setsig(i uint32, fn uintptr) {
	var sa sigactiont
	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
	sa.sa_mask = uint32(sigset_all)
	if fn == abi.FuncPCABIInternal(sighandler) { // abi。FuncPCABIInternal（sighandler）匹配signal_unix中的调用者。go 
		fn = abi.FuncPCABI0(sigtramp)
	}
	sa.sa_sigaction = fn
	sigaction(i, &sa, nil)
}

// go:nosplit 
// go:NOWRITEBRIERREC 
func setsigstack(i uint32) {
	throw("setsigstack")
}

// go:nosplit 
// go:NOWRITEBRIERREC 
func getsig(i uint32) uintptr {
	var sa sigactiont
	sigaction(i, nil, &sa)
	return sa.sa_sigaction
}

// setSignaltstackSP设置堆栈的ss_sp字段。
// go:nosplit 
func setSignalstackSP(s *stackt, sp uintptr) {
	s.ss_sp = sp
}

// go:nosplit 
// go:nowritebarrierrec 
func sigaddset(mask *sigset, i int) {
	*mask |= 1 << (uint32(i) - 1)
}

func sigdelset(mask *sigset, i int) {
	*mask &^= 1 << (uint32(i) - 1)
}

// go:nosplit 
func (c *sigctxt) fixsigcode(sig uint32) {
}

func setProcessCPUProfiler(hz int32) {
	setProcessCPUProfilerTimer(hz)
}

func setThreadCPUProfiler(hz int32) {
	setThreadCPUProfilerHz(hz)
}

// go nosplit 
func validSIGPROF(mp *m, c *sigctxt) bool {
	return true
}

var haveMapStack = false

func osStackAlloc(s *mspan) {
	// OpenBSD 6.4+要求堆栈与映射。
	// 切换到备用系统堆栈时，它将在系统调用、陷阱和
	// 的条目中检查此项。
	// 
	// 此函数在s用于任何数据之前调用，因此
	// 只需重新映射它就安全了。
	osStackRemap(s, _MAP_STACK)
}

func osStackFree(s *mspan) {
	// 撤消映射_堆栈。
	osStackRemap(s, 0)
}

func osStackRemap(s *mspan, flags int32) {
	if !haveMapStack {
		// 6.3之前的OpenBSD没有MAP_堆栈，因此
		// 以下mmap将失败。但是它也不需要MAP_STACK（显然），所以没有必要使用mmap。
		return
	}
	a, err := mmap(unsafe.Pointer(s.base()), s.npages*pageSize, _PROT_READ|_PROT_WRITE, _MAP_PRIVATE|_MAP_ANON|_MAP_FIXED|flags, -1, 0)
	if err != 0 || uintptr(a) != s.base() {
		print("runtime: remapping stack memory ", hex(s.base()), " ", s.npages*pageSize, " a=", a, " err=", err, "\n")
		throw("remapping stack memory failed")
	}
}

// go:nosplit 
func raise(sig uint32) {
	thrkill(getthrid(), int(sig))
}

func signalM(mp *m, sig int) {
	thrkill(int32(mp.procid), sig)
}

// sigPerThreadSyscall仅在linux上使用，因此我们分配了一个伪信号
// number。
const sigPerThreadSyscall = 1 << 31

// go:nosplit 
func runPerThreadSyscall() {
	throw("runPerThreadSyscall only valid on linux")
}
